#pragma once

#include <ActivScp.h>

#ifndef DEBUG_OLD_ACTIVESCRIPT

#define DEBUG_TRACE ATLTRACE
#define DEBUG_TRACE_END()

#else

CStringW __declspec(selectany) __debug, __debugTmp;
#define DEBUG_TRACE /*__debugTmp.Format(L"this(%08x) ", this); __debug+=__debugTmp;*/__debugTmp.Format
#define DEBUG_TRACE_END() __debug += __debugTmp;

class DomItem;

class eventProxy : public IDispatch
{
private:
	ULONG ulRef;
public:
	CComPtr<IDispatch> old;
	CComBSTR code;

	eventProxy() : ulRef(1) {}
	~eventProxy()
	{
		if (old) old.Release();
	}

public:
	STDMETHOD_(ULONG, AddRef)()
	{
		ULONG ref = InterlockedIncrement((LONG*)&ulRef);
		return ref;
	}
	STDMETHOD_(ULONG, Release)()
	{
		ULONG ref = InterlockedDecrement((LONG*)&ulRef);
		if (ref == 0)
		{
			ATLTRACE(L"eventProxy is deleted.\n");
			delete this;
		}
		return ref;
	}
	STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj)
	{
		if (!ppvObj)
			return E_POINTER;

		*ppvObj = NULL;
		if (IsEqualIID(iid, __uuidof(IUnknown)) ||
			IsEqualIID(iid, __uuidof(IDispatch)))
			*ppvObj = this;
		if (*ppvObj)
		{
			((LPUNKNOWN)(*ppvObj))->AddRef();
			return S_OK;
		}
		return E_NOINTERFACE;
	}
	STDMETHOD(GetTypeInfoCount)(UINT *pctinfo)
	{
		*pctinfo = 0;
		ATLTRACENOTIMPL(_T("eventProxy::GetTypeInfoCount"));
	}
	STDMETHOD(GetTypeInfo)(UINT /*iTInfo*/, LCID /*lcid*/, ITypeInfo **ppTInfo)
	{
		*ppTInfo = NULL;
		ATLTRACENOTIMPL(_T("eventProxy::GetTypeInfo"));
	}
	STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId)
	{
		ATLTRACENOTIMPL(_T("eventProxy::GetIDsOfNames"));
	}
	STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT *pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)
	{
		DEBUG_TRACE(L"Event Proxy begin: {%s}\n", code);DEBUG_TRACE_END();
		HRESULT hr = old->Invoke(dispIdMember, riid, lcid, wFlags, pdispparams, pVarResult, pExcepInfo, puArgErr);
		DEBUG_TRACE(L"Event Proxy end: {%s}\n", code);DEBUG_TRACE_END();
		return hr;
	}
};

class DomItem : public IDispatchEx
{
private:
	LONG ulRef;
	CComQIPtr<IDispatch> old;
	CComQIPtr<IDispatchEx> oldex;

protected:

protected:

public:
	CComBSTR name;
	DomItem(IUnknown* unk) : ulRef(1), old(unk), oldex(unk) {}
	~DomItem() {}

public:
	// IUnknown
	STDMETHOD_(ULONG, AddRef)()
	{
		ULONG ref = InterlockedIncrement((LONG*)&ulRef);
		return ref;
	}
	STDMETHOD_(ULONG, Release)()
	{
		ULONG ref = InterlockedDecrement((LONG*)&ulRef);
		if (ref == 0)
		{
			ATLTRACE(L"\t\t\tDomItem(%s) is deleted.\n", name);
			delete this;
		}
		return ref;
	}
	STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj)
	{
		if (!ppvObj)
			return E_POINTER;

		*ppvObj = NULL;
		if (IsEqualIID(iid, __uuidof(IUnknown)) ||
			IsEqualIID(iid, __uuidof(IDispatch)) ||
			(IsEqualIID(iid, __uuidof(IDispatchEx)) && oldex))
			*ppvObj = (IDispatchEx*)this;
		else
		{
			IID_ITridentEventSink;
			IID_IBindEventHandler;
			IID_IConnectionPointContainer;
			CComBSTR striid;
			if (iid == IID_ITridentEventSink)
				striid = L"IID_ITridentEventSink";
			else if (iid == IID_IBindEventHandler)
				striid = L"IID_IBindEventHandler";
			else if (iid == IID_IConnectionPointContainer)
				striid = L"IID_IConnectionPointContainer";
			else if (iid == IID_IHTMLWindow2)
				striid = L"IID_IHTMLWindow2";
			else if (iid == IID_IHTMLWindow3)
				striid = L"IID_IHTMLWindow3";
			else
			{
				LPOLESTR buf = NULL;
				StringFromIID(iid, &buf);
				if (buf)
				{
					striid = buf;
					CoTaskMemFree(buf);
				}
			}
			// {719C3050-F9D3-11CF-A493-00400523A8A0} what is it???
			HRESULT hr = old.p->QueryInterface(iid, ppvObj);
			DEBUG_TRACE(L"\t\t\tDomItem(%s)::QueryInterface(%s, *ppvObj(0x%08x)) - return %08x\n", name, (BSTR)striid, *ppvObj, hr); DEBUG_TRACE_END();
			return hr;
		}

		if (*ppvObj)
		{
			((LPUNKNOWN)(*ppvObj))->AddRef();
			return S_OK;
		}
		return E_NOINTERFACE;
	}

	// IDispatch
	STDMETHOD(GetTypeInfoCount)(UINT *pctinfo)
	{
		HRESULT hr = old->GetTypeInfoCount(pctinfo);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetTypeInfoCount(*pctinfo(%d))\n", name, *pctinfo); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetTypeInfo)(UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo)
	{
		HRESULT hr = old->GetTypeInfo(iTInfo, lcid, ppTInfo);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetTypeInfo(iTInfo(%d), lcid(%08x), *ppTInfo(0x%08x))\n", name, iTInfo, lcid, *ppTInfo); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID * rgDispId)
	{
		HRESULT hr = old->GetIDsOfNames(riid, rgszNames, cNames, lcid, rgDispId);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetIDsOfNames(riid, *rgszNames(%s), cNames(%d), lcid(%08x), *rgDispId(%d))\n", name, *rgszNames, cNames, lcid, *rgDispId); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT *pVarResult, EXCEPINFO * pExcepInfo, UINT * puArgErr)
	{
		CComVariant ret;
		HRESULT hr = old->Invoke(dispIdMember, riid, lcid, wFlags, pdispparams, &ret, pExcepInfo, puArgErr);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::Invoke(dispid(%d), riid, lcid(%08x), wFlags(%d), params(%d), pVarResult.VT(%d), pExcepInfo(%08x), *puArgErr(%d)\n", name,
					dispIdMember, lcid, wFlags, pdispparams->cArgs, ret.vt, pExcepInfo, puArgErr?*puArgErr:0);
		DEBUG_TRACE_END();
		if ((ret.vt==VT_DISPATCH) && V_DISPATCH(&ret))
		{
			CComBSTR member;
			if (oldex)
				oldex->GetMemberName(dispIdMember, &member);
			DomItem* p = new DomItem(V_DISPATCH(&ret));
			p->name = member;
			if (wFlags == DISPATCH_METHOD)
				p->name += L"()";
			ret.pdispVal->Release();
			ret.pdispVal = p;
		}
		if (pVarResult)
			ret.Detach(pVarResult);
		return hr;
	}

	// IDispatchEx
	STDMETHOD(DeleteMemberByDispID)(DISPID id)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->DeleteMemberByDispID(id);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::DeleteMemberByDispID(id(%d))\n", name, id); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(DeleteMemberByName)(BSTR bstrName, DWORD grfdex)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->DeleteMemberByName(bstrName, grfdex);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::DeleteMemberByName(bstrName(%s), grfdex(%08x))\n", name, bstrName, grfdex); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetDispID)(BSTR bstrName, DWORD grfdex, DISPID *pid)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->GetDispID(bstrName, grfdex, pid);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetDispID(bstrName(%s), grfdex(%08x), *pid(%d))\n", name, bstrName, grfdex, *pid); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetMemberName)(DISPID id, BSTR *pbstrName)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->GetMemberName(id, pbstrName);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetMemberName(id(%d), *pbstrName(%s))\n", name, id, *pbstrName); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetMemberProperties)(DISPID id, DWORD grfdexFetch, DWORD *pgrfdex)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->GetMemberProperties(id, grfdexFetch, pgrfdex);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetMemberProperties(id(%d), grfdexFetch(%08x), *pgrfdex(%08x))\n", name, id, grfdexFetch, *pgrfdex); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetNameSpaceParent)(IUnknown **ppunk)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->GetNameSpaceParent(ppunk);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetNameSpaceParent(*ppunk(0x%08x))\n", name, *ppunk); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetNextDispID)(DWORD grfdex, DISPID id, DISPID *pid)
	{
		ATLASSERT(oldex);
		HRESULT hr = oldex->GetNextDispID(grfdex, id, pid);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::GetNextDispID(grfdex(%08x), id(%d), *pid(%d))\n", name, grfdex, id, *pid); DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(InvokeEx)(DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller)
	{
		ATLASSERT(oldex);
		CComVariant ret;
		HRESULT hr = oldex->InvokeEx(id, lcid, wFlags, pdp, &ret, pei, pspCaller);
		DEBUG_TRACE(L"\t\t\tDomItem(%s)::InvokeEx(id(%d), lcid(%08x), wFlags(0x%x), params(%d), result.vt(%d), pExcepInfo(0x%08x), pspCaller(0x%08x)\n", name,
					id, lcid, wFlags, pdp->cArgs, ret.vt, pei, pspCaller);
		DEBUG_TRACE_END();
		if ((ret.vt == VT_DISPATCH) && V_DISPATCH(&ret))
		{
			CComBSTR member;
			oldex->GetMemberName(id, &member);
			DomItem* p = new DomItem(V_DISPATCH(&ret));
			p->name = member;
			if (wFlags == DISPATCH_METHOD)
				p->name += L"()";
			ret.pdispVal->Release();
			ret.pdispVal = p;
		}
		if (pvarRes)
			ret.Detach(pvarRes);
		return hr;
	}
};


class NewSite : public IActiveScriptSite
{
public:
	IActiveScriptSite* old;

	STDMETHOD_(ULONG, AddRef)() { return old->AddRef(); }
	STDMETHOD_(ULONG, Release)() { return old->Release(); }
	STDMETHOD(QueryInterface)(REFIID iid, LPVOID* ppvObj)
	{
		return old->QueryInterface(iid, ppvObj);
	}

	//IActiveScriptSite members
	STDMETHOD(GetLCID)(LCID  *plcid)
	{
		HRESULT hr = old->GetLCID(plcid);
		DEBUG_TRACE(L"\tScriptSite::GetLCID() - %08x\n", *plcid);
		DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(GetItemInfo)(LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti)
	{
		HRESULT hr = old->GetItemInfo(pstrName, dwReturnMask, ppiunkItem, ppti);
		DEBUG_TRACE(L"\tScriptSite::GetItemInfo() - %s - ReturnMask(%08x)\n", pstrName, dwReturnMask);
		DEBUG_TRACE_END();
		if ((dwReturnMask&SCRIPTINFO_IUNKNOWN) && *ppiunkItem)
		{
			DomItem* p = new DomItem(*ppiunkItem);
			p->name = pstrName;
			(*ppiunkItem)->Release();
			*ppiunkItem = p;
		}
		return hr;
	}

	STDMETHOD(GetDocVersionString)(BSTR  *pbstrVersion)
	{
		HRESULT hr = old->GetDocVersionString(pbstrVersion);
		DEBUG_TRACE(L"\tScriptSite::GetDocVersionString() - Version(%s)\n", *pbstrVersion);
		DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(OnScriptTerminate)(const VARIANT  *pvarResult,const EXCEPINFO  *pexcepinfo)
	{
		HRESULT hr = old->OnScriptTerminate(pvarResult, pexcepinfo);
		DEBUG_TRACE(L"\tScriptSite::OnScriptTerminate() - Result(%08x) - ExceptInfo(%08x)\n", pvarResult, pexcepinfo);
		DEBUG_TRACE_END();
		if (pexcepinfo)
		{
			DEBUG_TRACE(L"\t\tExcepInfo: wCode(%d) source(%s) description(%s)\n", pexcepinfo->wCode, pexcepinfo->bstrSource, pexcepinfo->bstrDescription);
			DEBUG_TRACE_END();
		}
		return hr;
	}
	STDMETHOD(OnStateChange)(SCRIPTSTATE ssScriptState)
	{
		HRESULT hr = old->OnStateChange(ssScriptState);
		DEBUG_TRACE(L"\tScriptSite::OnStateChange() - state(%d)\n", ssScriptState);
		DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(OnScriptError)(IActiveScriptError  *pscripterror)
	{
		HRESULT hr = old->OnScriptError(pscripterror);
		DEBUG_TRACE(L"\tScriptSite::OnScriptError() - IActiveScriptError(%08x)\n", pscripterror);
		DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(OnEnterScript)(void)
	{
		HRESULT hr = old->OnEnterScript();
		DEBUG_TRACE(L"\tScriptSite::OnEnterScript()\n");
		DEBUG_TRACE_END();
		return hr;
	}
	STDMETHOD(OnLeaveScript)(void)
	{
		HRESULT hr = old->OnLeaveScript();
		DEBUG_TRACE(L"\tScriptSite::OnLeaveScript()\n");
		DEBUG_TRACE_END();
		return hr;
	}
};

#endif // DEBUG_OLD_ACTIVESCRIPT
